package com.zbkj.service.assistant.service;


import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.zbkj.common.constants.AssistantConstants;
import com.zbkj.common.constants.RedisConstants;
import com.zbkj.common.dto.KeyNumDto;
import com.zbkj.common.model.assistant.AssistantConversation;
import com.zbkj.common.model.assistant.AssistantGroup;
import com.zbkj.common.model.assistant.AssistantGroupAccount;
import com.zbkj.common.model.assistant.AssistantSetting;
import com.zbkj.common.model.user.User;
import com.zbkj.common.utils.RedisUtil;
import com.zbkj.service.assistant.dao.AssistantConversationDao;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zbkj.service.assistant.model.request.AssistantConversationInitRequest;
import com.zbkj.service.assistant.model.request.AssistantMessageUserSendRequest;
import com.zbkj.service.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
* @author keNan
* @description AssistantConversationServiceImpl 接口实现
* @date 2025-06-11
*/
@Slf4j
@Service
public class AssistantConversationBaseServiceImpl extends ServiceImpl<AssistantConversationDao, AssistantConversation> {
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private AssistantGroupAccountBaseServiceImpl assistantGroupAccountBaseService;
    @Autowired
    private AssistantGroupBaseServiceImpl assistantGroupBaseService;
    @Autowired
    private AssistantMessageBaseServiceImpl assistantMessageBaseService;
    @Autowired
    private AssistantSettingBaseServiceImpl assistantSettingBaseService;
    @Autowired
    private UserService userService;

    public void updateAddMsgCount(Integer conversationId, Integer userCount, Integer serviceCount) {
        baseMapper.updateMsgCount(conversationId, userCount, serviceCount);
    }

    public Map<Integer, Integer> getCurrentServiceConversationCount() {
        return baseMapper.getCurrentServiceConversationCount()
            .stream()
            .collect(Collectors.toMap(KeyNumDto::getKeyId, KeyNumDto::getNum));
    }

    @Transactional
    public AssistantConversation getUserConversation(AssistantConversationInitRequest request, Integer userId) {
        // 查看是否有未结束的会话，有直接返回
        AssistantConversation one = super.lambdaQuery()
            .eq(AssistantConversation::getUserId, userId)
            .in(AssistantConversation::getStatus, AssistantConstants.Conversation.STATUS_SERVICE_LIST)
            .one();
        if (one != null) {
            return one;
        }

        /*
        需要创建会话，进行一系列判断和排队
        1. 判断是否被拉黑，如果被拉黑直接抛出异常
            用户拉黑提示：弹窗提示，不进入聊天页面
        2. 判断客服是否在上班时间，如果不在上班时间需要进行提示
            客服下班提示：进入聊天页面，会话状态是排队，有系统提示客服下班
        3. 进入排队，给出排队提示
        4. 内容发送流程，判断用户从哪进入的，需要发送一条消息关联的商品、订单、售后单的消息
            同时根据用户进入的入口，返回对应的分组类型
        5. 查看配置，查看该分组客服当前接待数量，查看接待人数有没有超标
            - 没超标，安排客服并更新会话为接待中
            - 已超标，查看默认组客服是否有空闲

         */

        // 判断是否被拉黑，如果被拉黑直接抛出异常
        this.checkUserIsBlack(userId);
        AssistantSetting setting = assistantSettingBaseService.getSetting();

        // 创建会话并加入队列，根据配置判断是否需要发送排队提示语
        AssistantConversation conversation = this.initAndJoinQueue(userId, setting, request);

        // 判断客服是否在上班时间，如果不在上班时间需要根据配置进行提示
        this.checkServiceIsWork(conversation, setting);

        // 内容发送流程，判断用户从哪进入的，需要发送一条消息关联的商品、订单、售后单的消息，并得到分组类型
        AssistantGroup.CodeEnum codeEnum = this.sendDefaultContent(userId, conversation);

        Integer serviceAccountId = this.checkServiceAccountId(setting, codeEnum);
        // 找到客服id，更新会话
        if (!AssistantConstants.INVALID_SERVICE_ACCOUNT_ID.equals(serviceAccountId)) {
            this.updateQueueToService(conversation, serviceAccountId, setting, true);
        }

        return super.getById(conversation.getId());
    }

    /**
     * 排队中转为服务中
     * @param conversation 会话
     * @param serviceAccountId 服务人员id
     */
    public void updateQueueToService(AssistantConversation conversation, Integer serviceAccountId, AssistantSetting setting, Boolean isPushToUser) {
        AssistantGroupAccount account = assistantGroupAccountBaseService.getByAccountId(serviceAccountId);
        super.lambdaUpdate()
            .eq(AssistantConversation::getId, conversation.getId())
            .set(AssistantConversation::getServiceAccountId, serviceAccountId)
            .set(AssistantConversation::getOriginalServiceAccountId, serviceAccountId)
            .set(AssistantConversation::getAllServiceAccountIds, serviceAccountId)
            .set(AssistantConversation::getStatus, AssistantConstants.Conversation.STATUS_SERVICE)
            .set(AssistantConversation::getStartTime, new Date())
            .set(AssistantConversation::getServiceName, account.getNickname())
            .set(AssistantConversation::getServiceAvatar, account.getAvatar())
            .update();
        AssistantConversation dbConversation = super.getById(conversation.getId());

        String content = StrUtil.format("已为您安排客服 {} 进行服务，请稍等...", account.getNickname());
        assistantMessageBaseService.sendBySystem(dbConversation, AssistantConstants.Message.CONTENT_TYPE_SYSTEM, content, isPushToUser, AssistantConstants.Message.CONTENT_TYPE_SERVICE_REFRESH_CONVERSATION);

        if (setting.getIsOpenFirstSentence() && ObjUtil.isNotEmpty(setting.getFirstSentence())) {
            assistantMessageBaseService.sendBySystem(dbConversation, AssistantConstants.Message.CONTENT_TYPE_SYSTEM, setting.getFirstSentence(), isPushToUser);
        }

    }

    /**
     * 创建会话并加入队列，根据配置判断是否需要发送排队提示语
     */
    private AssistantConversation initAndJoinQueue(Integer userId, AssistantSetting setting, AssistantConversationInitRequest request) {
        Integer source = ObjUtil.isNotEmpty(request.getSource())? request.getSource(): AssistantConstants.Conversation.SOURCE_DEFAULT;
        String sourceId = AssistantConstants.Conversation.SOURCE_DEFAULT.equals(source) ? "": request.getSourceId();
        User user = userService.getById(userId);
        AssistantConversation conversation = new AssistantConversation()
            .setUserId(userId)
            .setServiceAccountId(AssistantConstants.INVALID_SERVICE_ACCOUNT_ID)
            .setOriginalServiceAccountId(AssistantConstants.INVALID_SERVICE_ACCOUNT_ID)
            .setStatus(AssistantConstants.Conversation.STATUS_QUEUE)
            .setStartTime(new Date())
            .setSource(source)
            .setSourceId(sourceId)
            .setUserName(user.getNickname())
            .setUserAvatar(user.getAvatar());
        super.save(conversation);

        if (setting.getIsOpenQueueReminder()) {
            assistantMessageBaseService.sendBySystem(conversation, AssistantConstants.Message.CONTENT_TYPE_SYSTEM, "欢迎来到客服系统，您已进入到排队队列", true);
        }
        return conversation;
    }

    /**
     * 判断用户是否被拉黑，如果被拉黑直接抛出异常
     */
    private void checkUserIsBlack(Integer userId) {

    }

    /**
     * 判断客服是否在上班时间，如果不在上班时间需要根据配置进行提示
     */
    private void checkServiceIsWork(AssistantConversation conversation, AssistantSetting setting) {
        boolean isWork = true;
        if (!isWork && setting.getIsOpenOffWorkReminder()) {
            assistantMessageBaseService.sendBySystem(conversation, AssistantConstants.Message.SENDER_TYPE_SYSTEM, setting.getOffWorkReminder(), true);
        }
    }

    /**
     * 内容发送流程，判断用户从哪进入的，需要发送一条消息关联的商品、订单、售后单的消息，并返回分组类型
     */
    private AssistantGroup.CodeEnum sendDefaultContent(Integer userId, AssistantConversation conversation) {
        if (AssistantConstants.Conversation.SOURCE_PRODUCT.equals(conversation.getSource())) {
            AssistantMessageUserSendRequest req = new AssistantMessageUserSendRequest()
                .setContentType(AssistantConstants.Message.CONTENT_TYPE_PRODUCT)
                .setContent(conversation.getSourceId());
            assistantMessageBaseService.send(req, userId, false);
            return AssistantGroup.CodeEnum.BEFORE;

        } else if (AssistantConstants.Conversation.SOURCE_ORDER.equals(conversation.getSource())) {
            AssistantMessageUserSendRequest req = new AssistantMessageUserSendRequest()
                .setContentType(AssistantConstants.Message.CONTENT_TYPE_ORDER)
                .setContent(conversation.getSourceId());
            assistantMessageBaseService.send(req, userId, false);
            return AssistantGroup.CodeEnum.AFTER;

        } else if (AssistantConstants.Conversation.SOURCE_REFUND_ORDER.equals(conversation.getSource())) {
            AssistantMessageUserSendRequest req = new AssistantMessageUserSendRequest()
                .setContentType(AssistantConstants.Message.CONTENT_TYPE_REFUND_ORDER)
                .setContent(conversation.getSourceId());
            assistantMessageBaseService.send(req, userId, false);
            return AssistantGroup.CodeEnum.AFTER;

        }
        return AssistantGroup.CodeEnum.DEFAULT;
    }

    /**
     * 根据分配模式，找到一个合适的客服id
     */
    public Integer checkServiceAccountId(AssistantSetting setting, AssistantGroup.CodeEnum codeEnum) {
        AssistantGroup group = assistantGroupBaseService.getByCode(codeEnum);
        Map<Integer, Integer> countMap = this.getCurrentServiceConversationCount();

        Integer serviceAccountId = AssistantConstants.INVALID_SERVICE_ACCOUNT_ID;
        if (AssistantConstants.Setting.CONVERSATION_ASSIGN_MODE_AVG.equals(setting.getConversationAssignMode())) {
            serviceAccountId = getServiceAccountIdByModeAvg(group);
        } else if (AssistantConstants.Setting.CONVERSATION_ASSIGN_MODE_REMAINING.equals(setting.getConversationAssignMode())) {
            serviceAccountId = getServiceAccountIdByModeRemaining(group, countMap);
        }

        // 未找到合适的客服，返回无效客服id
        if (AssistantConstants.INVALID_SERVICE_ACCOUNT_ID.equals(serviceAccountId)) {
            return AssistantConstants.INVALID_SERVICE_ACCOUNT_ID;
        }

        // 判断是否超过接待人数
        Integer count = countMap.get(serviceAccountId);
        if (ObjUtil.isEmpty(count) || count < setting.getAssistantConcurrentNumber()) {
            // 没有超过接待人数，返回接待客服id
            return serviceAccountId;
        }

        // 超过接待人数，返回无效客服id
        return AssistantConstants.INVALID_SERVICE_ACCOUNT_ID;
    }

    /**
     * 以禁用客服的方式结束会话
     * @param accountId 客服人员id
     */
    public void processDisableAccount(Integer accountId) {
        List<AssistantConversation> list = super.lambdaQuery()
            .eq(AssistantConversation::getServiceAccountId, accountId)
            .eq(AssistantConversation::getStatus, AssistantConstants.Conversation.STATUS_SERVICE)
            .list();
        for (AssistantConversation conversation : list) {
            super.lambdaUpdate()
                .eq(AssistantConversation::getId, conversation.getId())
                .set(AssistantConversation::getEndType, AssistantConstants.Conversation.END_TYPE_DISABLE_ACCOUNT)
                .set(AssistantConversation::getStatus, AssistantConstants.Conversation.STATUS_COMPLETE)
                .set(AssistantConversation::getEndTime, new Date())
                .update();

            String content = "客服被禁用，会话已结束";
            assistantMessageBaseService.sendBySystem(conversation, AssistantConstants.Message.CONTENT_TYPE_SYSTEM, content, true, AssistantConstants.Message.CONTENT_TYPE_SERVICE_REFRESH_CONVERSATION);
        }
    }

    /**
     * 按余量分配：通过当前接待中的剩余分配量获取客服ID
     * 该组中的客服按照创建时间升序排序，记录顺序到redis中，逐个增长
     * @param group
     * @return
     */
    private Integer getServiceAccountIdByModeRemaining(AssistantGroup group, Map<Integer, Integer> countMap) {
        List<AssistantGroupAccount> accounts = assistantGroupAccountBaseService.findByGroupId(group.getId());
        Integer accountId = AssistantConstants.INVALID_SERVICE_ACCOUNT_ID;
        Integer count = 999999;
        for (AssistantGroupAccount account : accounts) {
            Integer i = countMap.get(account.getAccountId());
            if (ObjUtil.isEmpty(i)) {
                // 这个客服当前没有接待
                return account.getAccountId();
            }

            if (ObjUtil.isNotEmpty(i) && i < count) {
                count = i;
                accountId = account.getAccountId();
            }
        }
        return accountId;
    }

    /**
     * 平均分配：轮询分配
     * 该组中的客服按照创建时间升序排序，记录顺序到redis中，逐个增长
     * @param group
     * @return
     */
    private Integer getServiceAccountIdByModeAvg(AssistantGroup group) {
        Integer index = 0;
        Object o = redisUtil.get(RedisConstants.CONVERSATION_ASSIGN_MODE_AVG_INDEX_KEY);
        if (ObjUtil.isNotEmpty(o)) {
            index = (Integer) o;
        }

        List<AssistantGroupAccount> accounts = assistantGroupAccountBaseService.findByGroupId(group.getId());
        if (ObjUtil.isEmpty(accounts)) {
            return AssistantConstants.INVALID_SERVICE_ACCOUNT_ID;
        }

        if (index > accounts.size() - 1) {
            index = 0;
        }
        redisUtil.set(RedisConstants.CONVERSATION_ASSIGN_MODE_AVG_INDEX_KEY, ++index);
        return accounts.get(index).getAccountId();
    }

}

